package com.qf.face.utils;

import org.opencv.core.*;
import org.opencv.objdetect.CascadeClassifier;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferByte;
import java.io.File;
import java.lang.reflect.Field;

/**
 * @author 30909
 */
public class FaceUtil {
    /**
     * 判断一张图上有没有脸
     */
    public static BufferedImage haveFace(BufferedImage image) {
        //11.要加载的dll文佳到我们程序的系统配置里
        addDirToPath("C:\\opencv");
        //2.装载里面的类库
        System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
        //3.装在机器人
        CascadeClassifier faceDetector = new CascadeClassifier("C:\\opencv\\haarcascade_frontalface_alt.xml");
        //在openvc 中，认为一个图片是一个mat 对象
        //在java中，认为一个图片是一个bufferedImage对象

        //因此，我们需要把BufferedImage转化为Mat ，opencv 才能认得这个图片
        //检测完之后以后，它会裁剪Mat ，然后用 java 他把返回的是很好，需要Mat
        //在转回，BufferedImages
        Mat mat = bufferedImage2Mat(image);


        //创建一个人来区域的集合，诺是然连，就用矩形指出人脸所在的位置
        MatOfRect matOfRect = new MatOfRect();

        //让机器检测人脸，把人脸的记过 matOfRect 的集合中
        faceDetector.detectMultiScale(mat, matOfRect);
        //把结果打印出来
        Rect[] rects = matOfRect.toArray();

        if (rects == null || rects.length == 0) {
            //健壮性校验 图片上没有脸
            return null;
        }
        //计算出最大的脸
        Rect maxFace = null;
        int maxInt = 0;

        for (int i = 0; i < rects.length; i++) {
            Rect rect = rects[i];
            if (maxInt< rect.width* rect.height){
                //这个脸更大
                maxInt =rect.width*rect.height;
                maxFace =rect;
            }
        }


        //图片裁剪
        Mat face=new Mat(mat,maxFace);

        return mat2BufferedImage(face);
    }

    /**
     * 临时给jvm 添加环境变量，获取path对象
     */
    private static void addDirToPath(String path) {

        try {
            //反射classloader 里面的sys_paths
            Field field = ClassLoader.class.getDeclaredField("sys_paths");
            //修改这个成员
            field.setAccessible(true);
            //获取此变量的值
            String[] paths = (String[]) field.get(null);
            //创建一个数组比 paths 多以元素
            String[] temp = new String[paths.length + 1];
            //数组拷贝
       //  少个s   System.arraycopy(paths, 0, temp, 0, path.length());

            //数组复制
            System.arraycopy(paths, 0, temp, 0, paths.length);

            temp[temp.length - 1] = path;
            //设置 classloader 里面的 sys_paths 成员
            field.set(null, temp);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * BufferedImage 转 Mat
     */
    public static Mat bufferedImage2Mat(BufferedImage image) {
        //把BufferedImages 转化成像素点
        byte[] pixels = ((DataBufferByte) (image.getRaster().getDataBuffer())).getData();
        //java 像素点格式问题
        //java 颜色怎么表达
        //建立一个Mat
        Mat mat = new Mat(image.getHeight(), image.getWidth(), CvType.CV_8UC3);

        //把原本的 bufferedImages 里面的数据交给mat
        mat.put(0, 0, pixels);
        return mat;
    }

    /**
     * Mat 转 BufferedImages
     */
    public static BufferedImage mat2BufferedImage(Mat mat) {
        //获取图片的高，宽度
        int width = mat.cols();
        int height = mat.rows();

        //获取mat的颜色通道
        int dims = mat.channels();
        //rgb颜色通道，3个颜色通道，也就是说有三个字节来表达
        //一共有多少个 width * height 个像素点，一共有多少个字节呢？
        byte[] rgbData = new byte[dims * height * width];

        //获取像素点的信息
        mat.get(0, 0, rgbData);

        //申请一个 bufferedImages
        BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_BGR);

        //BufferedImage 的像素点
        int[] pixels = new int[width * height];
        //像素点转化算法
        int index = 0;
        int r = 0;
        int g = 0;
        int b = 0;
        for (int row = 0; row < height; row++) {
            for (int col = 0; col < width; col++) {
                //彩色照片
                if (dims == 3) {
                    //计算byte 数组的角标，像素行，列的关系
                    //拿到rgb
                    r = rgbData[index] & 0xff;
                    g = rgbData[index + 1] & 0xff;
                    b = rgbData[index + 2] & 0xff;

                    //三个像素点合成一个像素点
                    pixels[row * width + col] = (0xff << 24) | (r << 16) | (g << 8) | (b << 0);
                }
            }
        }
        image.getRaster().setDataElements(0, 0, width, height, pixels);
        return image;
    }

    public static void main(String[] args) throws Exception{
        BufferedImage image = ImageIO.read(new File("C:\\face\\dalaos.png"));
        image=haveFace(image);
        ImageIO.write(image,"png",new File("C:\\face\\dalaos_max_face.jpg"));
    }
}
